/********************************************************************************
 *    Copyright (C) 2014 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH    *
 *                                                                              *
 *              This software is distributed under the terms of the             *
 *              GNU Lesser General Public Licence (LGPL) version 3,             *
 *                  copied verbatim in the file "LICENSE"                       *
 ********************************************************************************/
#include "HiDaqUnpackTask.h"
#include "HiDaqInfoData.h"
#include "HiDaqUnpackUserFunction.h"
#include "HiDaqFormat.h"

#include "FairRootManager.h"
#include "FairRunAna.h"
#include "FairRuntimeDb.h"

#include <TClonesArray.h>
// ---- Default constructor -------------------------------------------
HiDaqUnpackTask::HiDaqUnpackTask()
    : FairTask("HiDaqUnpackTask")
{
    LOG(debug) << "Default Constructor of HiDaqUnpackTask";
}
// ---- Constructor with parameters (Optional) ---------------------------
HiDaqUnpackTask::HiDaqUnpackTask(string inputrawfile, Bool_t savetotree)
    : FairTask("HiDaqUnpackTask"),
    fInputRawFileName(inputrawfile),
    fSaveToTree(savetotree)
{
    LOG(debug) << "Constructor with parameters of HiDaqUnpackTask";
}

// ---- Destructor ----------------------------------------------------
HiDaqUnpackTask::~HiDaqUnpackTask() { 
    LOG(debug) << "Destructor of HiDaqUnpackTask"; 
}

// ----  Initialisation  ----------------------------------------------
void HiDaqUnpackTask::SetParContainers()
{
    LOG(debug) << "SetParContainers of HiDaqUnpackTask";
    // Load all necessary parameter containers from the runtime data base
    /*
    FairRunAna* ana = FairRunAna::Instance();
    FairRuntimeDb* rtdb=ana->GetRuntimeDb();
    
    <HiDaqUnpackTaskDataMember> = (<ClassPointer>*)
      (rtdb->getContainer("<ContainerName>"));
    */
}

// ---- Init ----------------------------------------------------------
InitStatus HiDaqUnpackTask::Init()
{
    LOG(debug) << "Initilization of HiDaqUnpackTask";

    // Get a handle from the IO manager
    FairRootManager* ioman = FairRootManager::Instance();
    if(ioman == 0)
    {
        LOG(error) << "FairRootManager not instantiated!";
        return kERROR;
    }
    // Open the input raw data file
    fInputFile = new std::ifstream(fInputRawFileName, std::ios::binary);
    if (!fInputFile->is_open()) {
        LOG(error) << "Can not open file: " << fInputRawFileName;
        return kERROR;
    }

    ReadHeader(fInputFile);

    // Create the TClonesArray for the output data and register
    // fDaqInfoArray = new TClonesArray("HiDaqInfoData", fMaxTSNum);
    fDaqInfoArray = new TClonesArray("HiDaqInfoData");
    LOG(debug) << "Max TS number set to: " << fMaxTSNum;
    ioman->Register("HiDaqInfoData", "HiDaq", fDaqInfoArray, kTRUE);

    // Do whatever else is needed at the initilization stage
    // Create histograms to be filled
    // initialize variables


    return kSUCCESS;
}

// ---- ReInit  -------------------------------------------------------
InitStatus HiDaqUnpackTask::ReInit()
{
    LOG(debug) << "Initilization of HiDaqUnpackTask";
    return kSUCCESS;
}

// ---- Exec ----------------------------------------------------------
void HiDaqUnpackTask::Exec(Option_t* /*option*/) { 
    LOG(debug) << "Exec of HiDaqUnpackTask"; 

    // Clear the output array
    // if(fDaqInfoArray == 0) LOG(error) << "fDaqInfoArray not initialized!";
    fDaqInfoArray->Clear();

    // ---- 解包第fCurrentTSIndex个时间片 -------------------------------------
    auto [Iter_tsID, Iter_offset] = fTSAddressList[fCurrentTSIndex];
    // ---- 找到正确的offset-----------------
    fInputFile->seekg(Iter_offset, std::ios::beg);
    // if(Iter_offset != fInputFile->tellg()) {
    //     LOG(warn) << "Seek to offset " << Iter_offset << " for TimeSlice " 
    //         << Iter_tsID << ", current position is " << fInputFile->tellg();
    //     fInputFile->seekg(Iter_offset, std::ios::beg);
    // }
    // ---- 解包时间片头部信息-----------------
    uint8_t TS_header[TS_HEAD]{}; // 帧头
    fInputFile->read(reinterpret_cast<char*>(&TS_header), TS_HEAD);
    uint32_t TS_length = static_cast<uint32_t>(readLE(fInputFile, TS_LENGTH)); // 帧长
    LOG(debug2) << "TimeSlice " << Iter_tsID << " length: " << TS_length;
	uint64_t TS_id = readLE(fInputFile, TS_ID); // 时间片编号
    uint8_t PXI_num = static_cast<uint8_t>(readLE(fInputFile, PXI_NUM)); // PXI数量
    if(TS_id != Iter_tsID) {
		LOG(error) << "TimeSlice header ID: " << TS_id << 
			" does not match expected: " <<Iter_tsID;
        fCurrentTSIndex++;
		return;
	}
    LOG(debug2) << "TimeSlice " << TS_id << " has " 
        << static_cast<int>(PXI_num) << " PXI(s).";
    // ---- 解包时间片内信息-----------------
    if(TS_header[0] == 0x13 && TS_header[1] == 0x80) {//非空帧
        // ---- 解包PXI内信息-----------------
        for(int i = 0; i < PXI_num; ++i) {
            char PXI_id[PXI_ID]{}; // PXI版号
            fInputFile->read(reinterpret_cast<char*>(&PXI_id), PXI_ID);
            uint32_t PXI_length = static_cast<uint32_t>(readLE(fInputFile, PXI_LENGTH)); // PXI数据块长度
            uint16_t FEE_num = static_cast<uint16_t>(readLE(fInputFile, FEE_NUM)); // FEE数量
			if(PXI_id[1] != 0x03) {
				LOG(error) << "Error: TimeSlice " << TS_id <<
					" has invalid PXI ID:" << int(PXI_id[1]);
				fCurrentTSIndex++;
		        return;
			}
            // ---- 解包FEE头部信息并存储信息--------------------
            for(int j = 0; j < FEE_num; ++j) {
                char FEE_id[FEE_ID]{}; // FEE版号
                fInputFile->read(reinterpret_cast<char*>(&FEE_id), FEE_ID);
                uint32_t FEE_length = static_cast<uint32_t>(readLE(fInputFile, FEE_LENGTH)); // FEE数据块长度
                HiDaqInfoData* daqInfo = new ((*fDaqInfoArray)[fDaqInfoArray->GetEntriesFast()]) 
                    HiDaqInfoData(TS_id,
                                PXI_num,
                                FEE_num,
                                PXI_id[0],
                                FEE_id[0],
                                fInputFile->tellg(), // FEE数据净荷起始地址
                                TS_length,
                                PXI_length,
                                FEE_length
                            );
                daqInfo->Print();
                // 跳过FEE数据块
                fInputFile->seekg(FEE_length, std::ios::cur);                    
            }
        }
    
    }
    else if(TS_header[0] == 0x17 && TS_header[1] == 0x80){//空帧
        HiDaqInfoData* daqInfo = new ((*fDaqInfoArray)[fDaqInfoArray->GetEntriesFast()]) 
            HiDaqInfoData(TS_id,
                        PXI_num,
                        0,
                        0,
                        0,
                        0, 
                        TS_length,
                        0,
                        0
                    );
        daqInfo->Print();
    }
    else{
		LOG(error) << "TimeSlice " << TS_id << " has invalid TS header.";
		fCurrentTSIndex++;
		return;
	}
    // ---- 读取时间片尾部信息 -------------------------------------    
    uint8_t TS_tail[TS_END]{}; // 帧尾
    fInputFile->read(reinterpret_cast<char*>(&TS_tail), TS_END);
	if(TS_tail[0] != 0x00 || TS_tail[1] != 0xC0) {
		LOG(error) << "TimeSlice " << TS_id << " has invalid TS tail.";
		fCurrentTSIndex++;
		return;
	}
    LOG_IF(info, fCurrentTSIndex%500 == 0)<< "Unpacked TimeSlice Number: " << fCurrentTSIndex + 1;
    fCurrentTSIndex++;
}

// ---- Finish --------------------------------------------------------
void HiDaqUnpackTask::Finish() { 
    LOG(debug) << "Finish of HiDaqUnpackTask";
    LOG(info)<< fCurrentTSIndex << " TimeSlices Unpacked.";
 
}

// ---- Methods --------------------------------------------------------
void HiDaqUnpackTask::ReadHeader(std::ifstream* fin) { 
    LOG(debug) << "Reading header of HiDaqUnpackTask"; 
    // 读取时间片个数
    fMaxTSNum = static_cast<int>(readLE(fin, FILE_TS_NUM));
    LOG(debug) << "Number of TimeSlices: " << fMaxTSNum;
    // 读取每个时间片的ID和偏移地址
    uint64_t TSTemp = 0, AddressTemp = 0;
    for (int i = 0; i < fMaxTSNum; ++i) {
        TSTemp = readLE(fin, FILE_TS_ID);
        AddressTemp = readLE(fin, FILE_TS_OFFSET);
        fTSAddressList.emplace_back(TSTemp, AddressTemp);
        LOG(debug2) << "TimeSlice ID: " << TSTemp << ", Offset: " << AddressTemp;
    }
}

ClassImp(HiDaqUnpackTask);
